﻿
Imports std
Imports lock_t = lock(Of locks.monitorlock)
Imports centrosphere
Imports command = centrosphere.job.storage.command
Imports update_key_mappings_switcher = switcher(Of Byte, std.pair(Of centrosphere.indexedJob, System.UInt32), Byte)

Partial Public Class storageJobRandomDistributor
    Private Shared ReadOnly insert_key_mapping_commands() As command = {command.write,
                                                                        command.uniqueWrite,
                                                                        command.modify}
    Private Shared ReadOnly erase_key_mapping_commands() As command = {command.delete}
    Private Shared ReadOnly success_insert_failed_erase_key_mapping_commands() As command = {command.read,
                                                                                             command.seek,
                                                                                             command.sizeof}
    Private Shared ReadOnly replace_key_mapping_commands() As command = {command.list}
    Private Shared ReadOnly clear_key_mapping_commands() As command = {command.retire,
                                                                       command.empty}
    Private Shared ReadOnly i64_0_clear_key_mapping_commands() As command = {command.keycount}

    Private Shared Function update_key_mapping_condition(ByVal cmd As Byte,
                                                         ByVal action As void(Of pair(Of indexedJob, UInt32))) _
                                                        As update_key_mappings_switcher.condition_action
        Return New update_key_mappings_switcher.condition_action(cmd,
                                                                 Function(ByRef r)
                                                                     action(r)
                                                                     Return 0
                                                                 End Function)
    End Function

    Private Sub key_mapping_action(ByRef arg As pair(Of indexedJob, UInt32),
                                   ByVal suc As void(Of storageJob, key_mapping),
                                   Optional ByVal fail As void(Of storageJob, key_mapping) = Nothing)
        assert(Not arg Is Nothing)
        assert(Not arg.first Is Nothing)
        assert(arg.second >= 0 AndAlso arg.second < key_mappings.size())
        assert(Not suc Is Nothing)
        If arg.first.finished() Then
            Dim sj As storageJob = Nothing
            sj = storageJob(arg.first)
            Dim km As key_mapping = Nothing
            km = key_mappings(arg.second)
            If sj.result Then
                void_(suc, unref(sj), unref(km))
            ElseIf Not fail Is Nothing Then
                void_(fail, unref(sj), unref(km))
            End If
        End If
    End Sub

    Private Sub key_mapping_action(ByRef arg As pair(Of indexedJob, UInt32),
                                   ByVal suc As void(Of String, key_mapping),
                                   Optional ByVal fail As void(Of String, key_mapping) = Nothing)
        assert(Not suc Is Nothing)
        key_mapping_action(arg,
                           Sub(ByRef sj As storageJob, ByRef km As key_mapping)
                               assert(Not sj Is Nothing)
                               suc(unref(sj.key), unref(km))
                           End Sub,
                           Sub(ByRef sj As storageJob, ByRef km As key_mapping)
                               If Not fail Is Nothing Then
                                   assert(Not sj Is Nothing)
                                   fail(unref(sj.key), unref(km))
                               End If
                           End Sub)
    End Sub

    Private Sub key_mapping_action(ByRef arg As pair(Of indexedJob, UInt32),
                                   ByVal suc As void(Of key_mapping),
                                   Optional ByVal fail As void(Of key_mapping) = Nothing)
        assert(Not suc Is Nothing)
        key_mapping_action(arg,
                           Sub(ByRef sj As storageJob, ByRef km As key_mapping)
                               suc(unref(km))
                           End Sub,
                           Sub(ByRef sj As storageJob, ByRef km As key_mapping)
                               If Not fail Is Nothing Then
                                   fail(unref(km))
                               End If
                           End Sub)
    End Sub

    Private Shared Sub insert_key_mapping(ByRef key As String, ByRef km As key_mapping)
        assert(km.insert(key))
    End Sub

    Private Sub insert_key_mapping(ByRef arg As pair(Of indexedJob, UInt32))
        key_mapping_action(arg, AddressOf insert_key_mapping)
    End Sub

    Private Shared Sub erase_key_mapping(ByRef key As String, ByRef km As key_mapping)
        km.erase(key)
    End Sub

    Private Sub erase_key_mapping(ByRef arg As pair(Of indexedJob, UInt32))
        key_mapping_action(arg, AddressOf erase_key_mapping)
    End Sub

    Private Shared Sub success_insert_failed_erase_key_mapping_suc(ByRef key As String, ByRef km As key_mapping)
        assert(km.insert(key))
    End Sub

    Private Shared Sub success_insert_failed_erase_key_mapping_fail(ByRef key As String, ByRef km As key_mapping)
        km.erase(key)
    End Sub

    Private Sub success_insert_failed_erase_key_mapping(ByRef arg As pair(Of indexedJob, UInt32))
        key_mapping_action(arg,
                           AddressOf success_insert_failed_erase_key_mapping_suc,
                           AddressOf success_insert_failed_erase_key_mapping_fail)
    End Sub

    Private Shared Sub replace_key_mapping(ByRef sj As storageJob, ByRef km As key_mapping)
        km.replace(sj.vs)
    End Sub

    Private Sub replace_key_mapping(ByRef arg As pair(Of indexedJob, UInt32))
        key_mapping_action(arg, AddressOf replace_key_mapping)
    End Sub

    Private Shared Sub clear_key_mapping(ByRef km As key_mapping)
        km.clear()
    End Sub

    Private Sub clear_key_mapping(ByRef arg As pair(Of indexedJob, UInt32))
        key_mapping_action(arg, AddressOf clear_key_mapping)
    End Sub

    Private Shared Sub i64_0_clear_key_mapping(ByRef sj As storageJob, ByRef km As key_mapping)
        If sj.i64 <= 0 Then
            km.clear()
        End If
    End Sub

    Private Sub i64_0_clear_key_mapping(ByRef arg As pair(Of indexedJob, UInt32))
        key_mapping_action(arg, AddressOf i64_0_clear_key_mapping, AddressOf i64_0_clear_key_mapping)
    End Sub

    Private Class key_mapping
        Private ReadOnly queue As jobqueue = Nothing
        Private ReadOnly update_interval_ms As Int64 = 0
        Private keys As [set](Of String) = Nothing
        Private l As lock_t

        Public Sub New(ByVal queue As jobqueue,
                       ByVal update_interval_ms As Int64)
            assert(Not queue Is Nothing)
            keys = New [set](Of String)()
            Me.queue = queue
            Me.update_interval_ms = update_interval_ms
            start()
        End Sub

        Private Sub start()
            Dim j As storageJob = Nothing
            assert_begin(New eventComb(Function() As Boolean
                                           j = New storageJob()
                                           j.command = command.list
                                           assert(j.valid())
                                           Return waitfor_ijob(j, queue) AndAlso
                                                  goto_next()
                                       End Function,
                                       Function() As Boolean
                                           If j.result AndAlso Not j.vs Is Nothing Then
                                               replace(j.vs)
                                           Else
                                               replace(Nothing)
                                           End If
                                           Return waitfor(update_interval_ms) AndAlso
                                                  goto_begin()
                                       End Function))
        End Sub

        Public Sub replace(ByVal vs As vector(Of String))
            If vs Is Nothing OrElse vs.empty() Then
                clear()
            Else
                Dim s As [set](Of String) = Nothing
                assert(vector2set(vs, s))
                keys = s
            End If
        End Sub

        Public Function [erase](ByVal key As String) As Boolean
            Return l.locked(Function() keys.erase(key))
        End Function

        Public Function insert(ByVal key As String) As Boolean
            Return l.locked(Function() keys.insert(key) <> keys.end())
        End Function

        Public Function contains_key(ByVal key As String) As Boolean
            Return l.locked(Function() keys.find(key) <> keys.end())
        End Function

        Public Sub clear()
            l.locked(Sub() keys.clear())
        End Sub
    End Class

    Private Function initial_key_mappings(ByVal update_interval_ms As Int64) As Boolean
        For i As Int64 = 0 To targetQueues().size() - 1
            key_mappings.push_back(New key_mapping(targetQueue(i), update_interval_ms))
        Next

        Return True
    End Function
End Class
